package file;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 文件拷贝.
 *
 * @author guanweichen
 * @version 2010-3-4,上午10:28:43
 */
public class CopyFileUtil {

    /** 拷贝文件时,使用的临时缓存大小 */
    private static final int BUFFER_SIZE = 2097152;

    /**
     * 拷贝文件目录.
     * 注意，该方法没有进行错误回复，也就是拷贝到一半时出错，不会删除已经拷贝的文件
     * 如果需要这样做，请自行实现
     * 
     * @param origDir 源目录
     * @param destDir 目标目录
     * @param mkdir true如果目标文件的路径不存在进行创建,否则不创建,抛出异常
     * @return 拷贝成功 true,否则false
     * @throws IOException
     */
    public static boolean copyDir(String origDir, String destDir, boolean mkdir) throws IOException {

        File pathFile = new File(destDir);
        if (!pathFile.exists() && mkdir) {
            // 文件路径不存在，并且确认需要创建文件路径
            if (!pathFile.mkdirs()) {
                throw new RuntimeException(destDir + " is can't create.");
            }
        }
        if (!pathFile.exists()){
            throw new FileNotFoundException(destDir);
        }
        copyDir(origDir, destDir);
        // 源文件夹
        File origFile = new File(origDir);
        // 拷贝完成目的文件夹
        String origDirName = getLastPathName(origDir);
        String destDirPath = destDir + File.separator + origDirName;
        File destFile = new File(destDirPath);
        // 大小相等拷贝完成
        return origFile.length() == destFile.length();
    }

    private static void copyDir(String origDir, String destDir) throws IOException {
        
        File origFile = new File(origDir);
        if (origFile.isFile()) {
            // 如果传入参数是文件,则直接拷贝
            copyFileByStream(origDir, destDir);
        } else if (origFile.isDirectory()) {
            // 如果传入参数是文件夹,则获得下一级子文件集合(包括文件夹和文件)
            File[] listFile = origFile.listFiles();
            // 创建目的文件夹
            String origDirName = getLastPathName(origDir);
            String destDirPath = destDir + File.separator + origDirName;
            File destFile = new File(destDirPath);
            if (!destFile.mkdir()) {
                throw new RuntimeException(destDirPath + " is can't create.");
            }
            for (File file : listFile) {
                if (file.isFile()) {
                    // 文件夹中的文件直接拷贝
                    copyFileByStream(file.getPath(), destDirPath);
                } else {
                    // 文件夹中的文件夹继续迭代
                    copyDir(file.getPath(), destDirPath);
                }
            }
        }
    }
    
    /**
     * nio拷贝，大文件拷贝时使用此方法比FileStreamCopy方法更快
     *
     * @param origFile 源文件
     * @param destDir 目标文件夹(/home/brian/temp/copy)
     * @param mkdir true如果目标文件的路径不存在进行创建,否则不创建,抛出异常
     * @return boolean If success return true
     * @throws IOException 
     */
    public static boolean copyFileByChannel(String origFile, String destDir,
            boolean mkdir) throws IOException {

        File pathFile = new File(destDir);
        if (!pathFile.exists() && mkdir) {
            // 文件路径不存在，并且确认需要创建文件路径
            if (!pathFile.mkdirs()) {
                throw new RuntimeException(destDir + " is can't create.");
            }
        }
        return copyFileByChannel(origFile, destDir);
    }

    /**
     * nio拷贝，大文件拷贝时使用此方法比FileStreamCopy方法更快
     *
     * @param origFile 源文件
     * @param destDir 目标文件夹(/home/brian/temp/copy)
     * @return boolean If success return true
     * @throws IOException 
     */
    private static boolean copyFileByChannel(String origFile, String destDir)
            throws IOException {

        File in = new File(origFile);
        File out = new File(destDir + File.separator + getLastPathName(origFile));
        FileInputStream fin = null;
        FileOutputStream fout = null;
        fin = new FileInputStream(in);
        fout = new FileOutputStream(out);
        FileChannel inc = fin.getChannel();
        FileChannel outc = fout.getChannel();
        ByteBuffer bb = ByteBuffer.allocateDirect(BUFFER_SIZE);
        while (inc.read(bb) != -1) {
            bb.flip();
            outc.write(bb);
            bb.clear();
        }
        fin.close();
        fout.flush();
        fout.close();
        return true;
    }

    /**
     * io stream拷贝
     *
     * @param origFile 源文件
     * @param destDir 目标文件夹(/home/brian/temp/copy)
     * @param mkdir true如果目标文件的路径不存在进行创建,否则不创建,抛出异常
     * @return boolean If success return true
     * @throws IOException 
     */
    public static boolean copyFileByStream(String origFile, String destDir,
            boolean mkdir) throws IOException {

        File pathFile = new File(destDir);
        if (!pathFile.exists() && mkdir) {
            // 文件路径不存在，并且确认需要创建文件路径
            if (!pathFile.mkdirs()) {
                throw new RuntimeException(destDir + " is can't create.");
            }
        }
        return copyFileByStream(origFile, destDir);
    }

    /**
     * io stream拷贝
     *
     * @param origFile 源文件
     * @param destDir 目标文件夹(/home/brian/temp/copy)
     * @return boolean If success return true
     * @throws IOException 
     */
    private static boolean copyFileByStream(String origFile, String destDir)
            throws IOException {

        File in = new File(origFile);
        File out = new File(destDir + File.separator + getLastPathName(origFile));
        FileInputStream fin = null;
        FileOutputStream fout = null;
        fin = new FileInputStream(in);
        fout = new FileOutputStream(out);
        byte[] buffer = new byte[BUFFER_SIZE];
        int ins;
        while ((ins = fin.read(buffer)) != -1) {
            fout.write(buffer, 0, ins);
        }
        fin.close();
        fout.flush();
        fout.close();
        return true;
    }

    /**
     * 获得路径最后的路径名称。
     *
     * @param path 路径
     * @return String last path name
     */
    private static String getLastPathName(String path) {

        return path.substring(path.lastIndexOf(File.separator) + 1);
    }

    public static void main(String args[]) throws Exception {

        String inFile = "/home/brian/temp/copy"; // 源文件
        String outDir1 = "/home/brian/temp/copy1"; // 输出文件1
//        String outDir2 = "/home/brian/temp/copy2"; // 输出文件2
        System.out.println(copyDir(inFile, outDir1, false));
//        copyFileByChannel(inFile, outDir1, true);
//        copyFileByStream(inFile, outDir2, true);
        
    }
}